/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#ifndef _INC_AK_EVT_
#define	_INC_AK_EVT_

#include "ak_types.h"
#include "ak_queue.h"
#include "ak_lock.h"
#include "ak_mem.h"
#include "ak_ie.h"
#include "ak_link.h"

#ifdef __cplusplus
extern "C" {
#endif

struct _t_sObject;
struct _t_sObjSlot;
struct _t_sEvent;
struct _t_sEvtQ;
struct _t_sEnv;

/* The Common Event:
** 1. Event's Source & Dest.
**	
** 2. Event's Type.
** 	bit 00-07	: Used by the Detail Sub.	
**  	bit 08-15	: Used by the Detail Sub.	
**	bit 16-23	: Used by the Obj Class, to Diff.
**	bit 24-31	: Used by the Obj Class, to Diff.
** 3. Event's Pri.
**	bit 00-07	: Used by the Detail Sub.
**	bit 08-15	: Used by the Priority Class.
** 	Critical 	: Highest, such as HW/SW Error, fatal, excption.
**	High 	: such as Timer, Qos-Required.
**	Medium 	: normal, most event used this Pri-Class.
**	Low 		: back-ground. the Low-Pri event may be execute delayed.
** 4. Event's Run-time Enviorment.
**	Each event is execute under an Run-time Enviorment. such as Task.
**	Event-Q is binding to that. (in most case, it's the Task Env)
**	We could do Enviorment check by Checking the Event-Q's & Object's to make Lock-Free.
**
*/

/* The Event Pool */
#define _SEVT_SLAB_MAX 		32 			/* 256 * (2^16) = 16M */
#define _SEVT_SLAB_MINSZ 	256 		/* <= MinSz, We Use the Fixed Pool */

typedef struct{
	/* IE Pool:
	** 1. Simple Pool for Fixed Size Job.
	** 2. Adding the Slab Pool to Support Diff Large Message. 
	*/
	size_t 				slabs[_SEVT_SLAB_MAX];
	_t_sysMSlot 		slots[_SEVT_SLAB_MAX];
	_t_sysMPool 		mPool;
	_t_sysMHeap 		mHeap;
	void		*		(*evtAlloc)();
	int 				(*evtFree )();
}_t_sEvtPool;

extern	int 	SEvtPool_InitEx(_t_sEvtPool * pEvtPool, int mPage);
extern	int 	SEvtPool_InitSi(_t_sEvtPool * pEvtPool, size_t mLimit,  int itSz);


/* The Enviorment:
** 1. Event's Enviorment.
** 2. IPC Mode.	
*/
#define 	ENV_IPC_EVTQ 	0
#define 	ENV_IPC_SOCKET 	1

/* The Env must be Present as an Array */
typedef struct _t_sEnv_Desc{
	/* IPC Mode */
	int				id;
	int 			ipc;
	int 			listen;
	int 			reserv;
	_u_ipport 		ipPort;

	/* Lookup Func */
	void 		*(*	lookup)(unsigned int, unsigned int);	/* Use the Id to Find the Object With EType */
}_t_sEnv_Desc;

#define SEnv_DescInit(pDesc, vId, vIPC, vIPPort, vListen, fLookUp) \
	do{\
		(pDesc)->id 	= (int)(vId);\
		(pDesc)->ipc 	= (int)(vIPC);\
		(pDesc)->listen	= (int)(vListen);\
		(pDesc)->reserv	= 0;\
		_IPPORT_ASSIGN(&((pDesc)->ipPort), &(vIPPort));\
		(pDesc)->lookup = fLookUp; \
	}while(0)

typedef struct _t_sEnv{
	/* Wake Up */
	_t_sysSemB 		* 	wake;
	_t_sEnv_Desc 		desc;
	
	/* Event Pool */
	_t_sEvtPool 	* 	pEvtPool;
	#if 1
	struct _t_sEvtQ * 	pEvtQ;
	#endif
	_t_ioLinkCi 	* 	pLink;
}_t_sEnv;




#define _SEvent_Pri_Critical 	0X00 
#define _SEvent_Pri_High 		0X01 
#define _SEvent_Pri_Medium 		0X02 
#define _SEvent_Pri_Low 		0X03 
#define _SEvent_Pri_Max		 	0X04 

/* Used for Statistic, Debug */
typedef struct{
	unsigned short 	pri;
	unsigned short 	reserv;
	unsigned int 	type;	/* Event Type 	*/
	const char 	*	name; 	/* Event Name 	*/ 	
}_t_sEvtDesc;

/* The Simple Marco to Define a Description */
#define TYPE_SEVT_DESC(desc, pri) {pri, desc, #desc}

/* 
Dynamic :
1. Si -> Msg = Len
2. Mi -> Mi - Msg = Len

Ex:
1. Mi -> Mi - Evt - Msg = Len
*/
typedef struct _t_sEvent{
	#if 0
	_t_sysMi			mi;
	#endif
	/* The Event Content is Encap by the IE:
	** Event Type	: 32-Bit, ie.iParam;
	** Event Pri	: 16-Bit, ie.iParam;
	*/
	_t_sIE 				ie;
	
	/* The Dest Object will should receive the Event:
	** Not Store the Src Object, the Event May be Raised by the Stub. 
	** Which is just a SAP, TNL, not an Object.
	*/
	unsigned short 	srcEnv;		/* Optional */
	unsigned short 	dstEnv;		/* Must Inform */

	unsigned int 	srcObj;		/* Optional */
	unsigned int 	dstObj;		/* Must Inform */

	unsigned int 	cursor; 	/* For Multi Cast Use */
	unsigned int 	casId;
}_t_sEvent;

#define _SEVT_EX_EV 		(sizeof(_t_sEvent))
#define _SEVT_EX_MI 		(sizeof(_t_sysMi))

/* Marcos for Event's Init /Set IE: InfoElement */
#define _SEvent_Init(pEvent, dEnv, dObj, eType, ePri)\
do{\
	_SIE_Init(&((pEvent)->ie));\
	(pEvent)->srcEnv 	= 0;\
	(pEvent)->dstEnv 	= dEnv;\
	(pEvent)->srcObj 	= 0;\
	(pEvent)->dstObj 	= dObj;\
	(pEvent)->cursor 	= 0;\
	(pEvent)->casId 	= 0;\
	_SEvent_SetPri(pEvent, ePri);\
	_SEvent_SetType(pEvent, eType);\
}while(0)

#define _SEvent_Copy(pDst, pSrc)\
do{\
	SYS_MEMCPY(pDst,pSrc, sizeof(_t_sEvent));\
}while(0)

/* Set the Event's Pri, Type, Obj, Content */
#define _SEvent_Assign(pDst, pSrc)\
do{\
	(pDst)->srcObj = (pSrc)->srcObj;\
	(pDst)->dstObj = (pSrc)->dstObj;\
	_SIE_Set(&((pDst)->ie), &((pSrc)->ie));\
}while(0)

/* Set the Event's Pri, Type, Obj */
#define _SEvent_SetParam(pDst, pSrc)\
do{\
	(pDst)->srcObj = (pSrc)->srcObj;\
	(pDst)->dstObj = (pSrc)->dstObj;\
	_SIE_SetParam(&((pDst)->ie), &((pSrc)->ie));\
}while(0)

/* The Priority of the Event : */
#define _SEvent_GetPri(pEvent) 	((pEvent)->ie.cont.sParam)
#define _SEvent_SetPri(pEvent, priority)	\
do{\
	_SEvent_GetPri(pEvent) = priority;		\
}while(0)

/* The Type & Class of the Event : */
#define _SEvent_GetType(pEvent) 		((pEvent)->ie.cont.iParam)
#define _SEvent_SetType(pEvent, type) 		\
do{\
	_SEvent_GetType(pEvent) = (int)(type);	\
}while(0)

#define _SEvent_SetEnvDst(pEvent, envDst)\
do{\
	(pEvent)->dstEnv 	= envDst;\
}while(0)

#define _SEvent_Class(pEvent) 		((pEvent)->ie.cont.iParam >> 16)

/* The MType of the Event : */
#define _SEvent_MType(pEvent) 		((pEvent)->ie.cont.mType)
#define _SEvent_VType(pEvent) 		((pEvent)->ie.cont.vType)

#define _SEvent_MsgPtr(pEvent) 		((pEvent)->ie.cont.msg.ptr)
#define _SEvent_MsgLen(pEvent) 		((pEvent)->ie.cont.msg.len)



/* The Event-Q :
** For EventQ, We Adopt the design of the JobQ, excption :
** 1. The Pool, The design is to distinguish the POOL implemention. thus we could use the Global POOL.
** 2. Now we don't Alloc the node! Just back the Old favor to Copy the Event itself.
** 3. Still Ring-Buf. but reduce the Process's copy by introduce the exeIx. (wrIx -- ExIx -- rdIx), after 
** execute, we update the rdIx. As we'll use the Huge Ring.
** 4. For Critical / High/ Low Priority, the Event is Queued. (As Most is Sync Proceed). 
**	Thus the Event should be Alloced & Copied.
** 5. For Normal Priority, the Event is Put into the Ring. The Event is Copied, Not Alloc.
*/
#define _SEvtRing_Gap 			4
#define _SEvtBat_Max			256

#define _SEvtPost_Retry 		(0X2)

typedef struct{
	/* Wake & Lock */
	
	_t_sysSemB 		* 	wake;
	_t_sysCS 			lock;

	/* Rd/Wr Ix */
	unsigned int		rdIx;
	unsigned int		wrIx; 
	
	/* Ring Buf */
	unsigned int		count;
	_t_sEvent 		* 	ring;

	/* Sche */
	unsigned int		batch;
	unsigned int		eStat;

	/* Event Entry : Handle */
	_t_cbentry 			eProc;
}_t_sEvtRing;

#define	_SEvtRing_Count(pEvtRing)			(((pEvtRing)->wrIx + (pEvtRing)->count - (pEvtRing)->rdIx)%((pEvtRing)->count))
#define	_SEvtRing_IsEmpty(pEvtRing)			(((pEvtRing)->rdIx) == ((pEvtRing)->wrIx))
#define _SEvtRing_IsFull(pEvtRing) 			((((pEvtRing)->wrIx + _SEvtRing_Gap)% (pEvtRing)->count) == (pEvtRing)->rdIx)
#define _SEvtRing_IsBusy(pEvtRing) 			(_SEvtRing_Count(pEvtRing) >= ((3 * (pEvtRing)->count)/4))

#if 0
#define _SEvtRing_Get(pEvtRing, pDst, pSrc)\
do{\
	pDst = (pEvtRing)->ring + (pEvtRing)->rdIx;\
	_SEvent_Assign(pDst, pSrc);\
	(pEvtRing)->rdIx = ((pEvtRing)->rdIx + 1)% (pEvtRing)->count;\
}while(0)
#endif

#define _SEvtRing_Put(pEvtRing, pDst, pSrc, vRet)\
do{\
	vRet = -1;\
	if(!(_SEvtRing_IsFull(pEvtRing))){\
		pDst = (pEvtRing)->ring +(pEvtRing)->wrIx;\
		_SEvent_Copy(pDst, pSrc);\
		(pEvtRing)->wrIx = ((pEvtRing)->wrIx + 1)% (pEvtRing)->count;\
		vRet = 0;\
	}\
}while(0)

#define _SEvtRing_Wake(pEvtRing) 	\
do{ \
	SYS_SEM_B_GIVE((pEvtRing)->wake);\
}while(0)

/* When EnQue Fail, Rem to Fail-Proc the Event */
#define _SEvtRing_EnQue(pEvtRing, pDst, pSrc, vRet) \
do{ \
	SYS_ENTER_CS(&((pEvtRing)->lock));	\
	_SEvtRing_Put((pEvtRing), pDst, pSrc, vRet);\
	SYS_LEAVE_CS(&((pEvtRing)->lock));	\
	_SEvtRing_Wake(pEvtRing);	\
}while(0)

typedef struct _t_sEvtQ{
	/* The EvtQ Type: Will be Checked as the Event is Async Disp. */
	int 			type;

	/* Enviorment : Will be Checked as the Event is Async Disp, will Force a Sync Event into the Async.
	** 1. Src Obj.Env 	= 	Dst Obj.Env (Sync ).
	** 2. Src Obj.Env 	!=	Dst Obj.Env (Async).
	** How to Get an Object's Env.
	** Obj.Env = Obj.EvtQ->pEnv.
	*/
	_t_sEnv		* 	pEnv;
	
	/* The Ring of Diff Priority */
	_t_sEvtRing 	pri[_SEvent_Pri_Max];
}_t_sEvtQ;



/* Event Que Interface */ 
extern	int 	SEvtQ_SetTraceLvl(int lvl);
extern	int 	SEvtQ_GetTraceLvl(void);

extern	int 	SEvtQ_Dump(_t_sEvtQ * pEvtQ, char * outBuf, int bufLen);
extern	int		SEvtQ_Peek(_t_sEvtQ * pEvtQ);
extern	int		SEvtQ_Sche(_t_sEvtQ * pEvtQ);
extern	int 	SEvtQ_Init(_t_sEvtQ * pEvtQ, _t_sEnv * pEnv, _t_cbentry * eProc[_SEvent_Pri_Max], unsigned int ringCnt[_SEvent_Pri_Max]);


/* Event Post Interface, External :
** Ret Value:
** -1 	: Failure
** 0		: Success
*/
extern	int 	SEvent_PostMsg(_t_sEnv * pEnv, _t_sEvent * pIn, void * pMsg, unsigned int lMsg);

extern	int 	SEnv_Init(_t_sEnv * pEnv, _t_sEnv_Desc * pDesc, _t_sysSemB * pWake, _t_sEvtQ * pEvtQ, _t_sEvtPool * pPool, _t_ioLinkCi * pLink);


#ifdef __cplusplus
}
#endif

#endif


